<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Mail
 * @subpackage Transport
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Abstract.php 18760 2009-10-31 13:09:01Z yoshida@zend.co.jp $
 */


/**
 * @see Zend_Mime
 */
require_once 'Zend/Mime.php';


/**
 * Abstract for sending eMails through different
 * ways of transport
 *
 * @category   Zend
 * @package    Zend_Mail
 * @subpackage Transport
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Mail_Transport_Abstract
{
	/**
	 * Mail body
	 * @var string
	 * @access public
	 */
	public $body = '';

	/**
	 * MIME boundary
	 * @var string
	 * @access public
	 */
	public $boundary = '';

	/**
	 * Mail header string
	 * @var string
	 * @access public
	 */
	public $header = '';

	/**
	 * Array of message headers
	 * @var array
	 * @access protected
	 */
	protected $_headers = array();

	/**
	 * Message is a multipart message
	 * @var boolean
	 * @access protected
	 */
	protected $_isMultipart = false;

	/**
	 * Zend_Mail object
	 * @var false|Zend_Mail
	 * @access protected
	 */
	protected $_mail = false;

	/**
	 * Array of message parts
	 * @var array
	 * @access protected
	 */
	protected $_parts = array();

	/**
	 * Recipients string
	 * @var string
	 * @access public
	 */
	public $recipients = '';

	/**
	 * EOL character string used by transport
	 * @var string
	 * @access public
	 */
	public $EOL = "\r\n";

	/**
	 * Send an email independent from the used transport
	 *
	 * The requisite information for the email will be found in the following
	 * properties:
	 *
	 * - {@link $recipients} - list of recipients (string)
	 * - {@link $header} - message header
	 * - {@link $body} - message body
	 */
	abstract protected function _sendMail();

	/**
	 * Return all mail headers as an array
	 *
	 * If a boundary is given, a multipart header is generated with a
	 * Content-Type of either multipart/alternative or multipart/mixed depending
	 * on the mail parts present in the {@link $_mail Zend_Mail object} present.
	 *
	 * @param string $boundary
	 * @return array
	 */
	protected function _getHeaders($boundary)
	{
		if (null !== $boundary) {
			// Build multipart mail
			$type = $this->_mail->getType();
			if (!$type) {
				if ($this->_mail->hasAttachments) {
					$type = Zend_Mime::MULTIPART_MIXED;
				} elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) {
					$type = Zend_Mime::MULTIPART_ALTERNATIVE;
				} else {
					$type = Zend_Mime::MULTIPART_MIXED;
				}
			}

			$this->_headers['Content-Type'] = array(
			$type . ';'
			. $this->EOL
			. " " . 'boundary="' . $boundary . '"'
			);
			$this->boundary = $boundary;
		}

		$this->_headers['MIME-Version'] = array('1.0');

		return $this->_headers;
	}

	/**
	 * Prepend header name to header value
	 *
	 * @param string $item
	 * @param string $key
	 * @param string $prefix
	 * @static
	 * @access protected
	 * @return void
	 */
	protected static function _formatHeader(&$item, $key, $prefix)
	{
		$item = $prefix . ': ' . $item;
	}

	/**
	 * Prepare header string for use in transport
	 *
	 * Prepares and generates {@link $header} based on the headers provided.
	 *
	 * @param mixed $headers
	 * @access protected
	 * @return void
	 * @throws Zend_Mail_Transport_Exception if any header lines exceed 998
	 * characters
	 */
	protected function _prepareHeaders($headers)
	{
		if (!$this->_mail) {
			/**
			 * @see Zend_Mail_Transport_Exception
			 */
			require_once 'Zend/Mail/Transport/Exception.php';
			throw new Zend_Mail_Transport_Exception('Missing Zend_Mail object in _mail property');
		}

		$this->header = '';

		foreach ($headers as $header => $content) {
			if (isset($content['append'])) {
				unset($content['append']);
				$value = implode(',' . $this->EOL . ' ', $content);
				$this->header .= $header . ': ' . $value . $this->EOL;
			} else {
				array_walk($content, array(get_class($this), '_formatHeader'), $header);
				$this->header .= implode($this->EOL, $content) . $this->EOL;
			}
		}

		// Sanity check on headers -- should not be > 998 characters
		$sane = true;
		foreach (explode($this->EOL, $this->header) as $line) {
			if (strlen(trim($line)) > 998) {
				$sane = false;
				break;
			}
		}
		if (!$sane) {
			/**
			 * @see Zend_Mail_Transport_Exception
			 */
			require_once 'Zend/Mail/Transport/Exception.php';
			throw new Zend_Mail_Exception('At least one mail header line is too long');
		}
	}

	/**
	 * Generate MIME compliant message from the current configuration
	 *
	 * If both a text and HTML body are present, generates a
	 * multipart/alternative Zend_Mime_Part containing the headers and contents
	 * of each. Otherwise, uses whichever of the text or HTML parts present.
	 *
	 * The content part is then prepended to the list of Zend_Mime_Parts for
	 * this message.
	 *
	 * @return void
	 */
	protected function _buildBody()
	{
		if (($text = $this->_mail->getBodyText())
		&& ($html = $this->_mail->getBodyHtml()))
		{
			// Generate unique boundary for multipart/alternative
			$mime = new Zend_Mime(null);
			$boundaryLine = $mime->boundaryLine($this->EOL);
			$boundaryEnd  = $mime->mimeEnd($this->EOL);

			$text->disposition = false;
			$html->disposition = false;

			$body = $boundaryLine
			. $text->getHeaders($this->EOL)
			. $this->EOL
			. $text->getContent($this->EOL)
			. $this->EOL
			. $boundaryLine
			. $html->getHeaders($this->EOL)
			. $this->EOL
			. $html->getContent($this->EOL)
			. $this->EOL
			. $boundaryEnd;

			$mp           = new Zend_Mime_Part($body);
			$mp->type     = Zend_Mime::MULTIPART_ALTERNATIVE;
			$mp->boundary = $mime->boundary();

			$this->_isMultipart = true;

			// Ensure first part contains text alternatives
			array_unshift($this->_parts, $mp);

			// Get headers
			$this->_headers = $this->_mail->getHeaders();
			return;
		}

		// If not multipart, then get the body
		if (false !== ($body = $this->_mail->getBodyHtml())) {
			array_unshift($this->_parts, $body);
		} elseif (false !== ($body = $this->_mail->getBodyText())) {
			array_unshift($this->_parts, $body);
		}

		if (!$body) {
			/**
			 * @see Zend_Mail_Transport_Exception
			 */
			require_once 'Zend/Mail/Transport/Exception.php';
			throw new Zend_Mail_Transport_Exception('No body specified');
		}

		// Get headers
		$this->_headers = $this->_mail->getHeaders();
		$headers = $body->getHeadersArray($this->EOL);
		foreach ($headers as $header) {
			// Headers in Zend_Mime_Part are kept as arrays with two elements, a
			// key and a value
			$this->_headers[$header[0]] = array($header[1]);
		}
	}

	/**
	 * Send a mail using this transport
	 *
	 * @param  Zend_Mail $mail
	 * @access public
	 * @return void
	 * @throws Zend_Mail_Transport_Exception if mail is empty
	 */
	public function send(Zend_Mail $mail)
	{
		$this->_isMultipart = false;
		$this->_mail        = $mail;
		$this->_parts       = $mail->getParts();
		$mime               = $mail->getMime();

		// Build body content
		$this->_buildBody();

		// Determine number of parts and boundary
		$count    = count($this->_parts);
		$boundary = null;
		if ($count < 1) {
			/**
			 * @see Zend_Mail_Transport_Exception
			 */
			require_once 'Zend/Mail/Transport/Exception.php';
			throw new Zend_Mail_Transport_Exception('Empty mail cannot be sent');
		}

		if ($count > 1) {
			// Multipart message; create new MIME object and boundary
			$mime     = new Zend_Mime($this->_mail->getMimeBoundary());
			$boundary = $mime->boundary();
		} elseif ($this->_isMultipart) {
			// multipart/alternative -- grab boundary
			$boundary = $this->_parts[0]->boundary;
		}

		// Determine recipients, and prepare headers
		$this->recipients = implode(',', $mail->getRecipients());
		$this->_prepareHeaders($this->_getHeaders($boundary));

		// Create message body
		// This is done so that the same Zend_Mail object can be used in
		// multiple transports
		$message = new Zend_Mime_Message();
		$message->setParts($this->_parts);
		$message->setMime($mime);
		$this->body = $message->generateMessage($this->EOL);

		// Send to transport!
		$this->_sendMail();
	}
}
